home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / nn.zip / ADMIN.C < prev    next >
C/C++ Source or Header  |  1989-12-31  |  21KB  |  1,002 lines

  1. /*
  2.  * nnadmin - nn administator program
  3.  */
  4.  
  5. #include <signal.h>
  6. #include <errno.h>
  7. #include "config.h"
  8. #include "db.h"
  9. #include "term.h"
  10.  
  11. static char *pre_input;
  12. static int verbose = 1;
  13.  
  14. extern int group_completion();
  15.  
  16.  
  17. static get_cmd(prompt1, prompt2)
  18. char *prompt1, *prompt2;
  19. {
  20.     char c;
  21.  
  22.     if (s_hangup) {
  23.     printf("\nnnmaster hangup\n");
  24.     nn_exit(0);
  25.     }
  26.  
  27.     if (pre_input) {
  28.     if ((c = *pre_input++) == NUL)
  29.         nn_exit(0);
  30.     } else {
  31.     if (prompt1) printf("\r%s\n", prompt1);
  32.     if (prompt2) printf("\r%s >>>", prompt2);
  33.     fl;
  34.         raw();
  35.     c = get_c();
  36.     unset_raw();
  37.     if (c == K_interrupt)
  38.         s_keyboard++;
  39.     else
  40.         printf("%c\n\n\r", c);
  41.     }
  42.     
  43.     if (islower(c)) 
  44.     c = toupper(c);
  45.     
  46.     return c;
  47. }
  48.  
  49.  
  50. static long get_entry(prompt_str, min_val, max_val)
  51. char *prompt_str;
  52. long min_val, max_val;
  53. {
  54.     char buf[100];
  55.     long val;
  56.     
  57.  loop:
  58.     
  59.     printf("%s %ld..%ld (or all): ", prompt_str, min_val, max_val);
  60.     fl;
  61.     gets(buf);
  62.     if (buf[0] == 'a' || buf[0] == NUL)
  63.     return -1L;
  64.  
  65.     val =  atol(buf);
  66.     if (val < min_val || val > max_val) goto loop;
  67.     
  68.     return val;
  69. }
  70.  
  71.  
  72. static admin_confirm(action)
  73. char *action;
  74. {
  75.     char buffer[100];
  76.  
  77.     if (pre_input) return 1;
  78.     
  79.     sprintf(buffer, "Confirm %s  Y)es N)o", action);
  80.     
  81.     return get_cmd((char *)NULL, buffer) == 'Y';
  82. }
  83.  
  84. char *get_groupname()
  85. {
  86.     char * groupname;
  87.     
  88.     raw();
  89.     printf("Group: ");
  90.     fl;
  91.     groupname = get_s(NONE, NONE, NONE, group_completion);
  92.     unset_raw();
  93.  
  94.     putchar(NL); putchar(CR);
  95.     
  96.     if (groupname == NULL) return NULL;
  97.     
  98.     if (groupname[0]) return groupname;
  99.  
  100.     if (current_group == NULL) return NULL;
  101.     if (current_group->group_flag & G_FOLDER) return NULL;
  102.     
  103.     return current_group->group_name;
  104. }
  105.  
  106.  
  107. admin_mode(input_string)
  108. char *input_string;
  109. {
  110.     int was_raw = unset_raw();
  111.     
  112.     if (input_string && *input_string) {
  113.     pre_input = input_string;
  114.     } else {
  115.     pre_input = NULL;
  116.     printf("\nMaster is%s running\n",
  117.            access(relative(lib_directory, "MPID"), 0) ? " NOT" : "");
  118.     }
  119.     
  120.     for (;;) {
  121.     switch(get_cmd(
  122. "\nC)onf E)xpire G)roups I)nit L)og M)aster Q)uit S)tat U)pdate V)alidate W)akeup",
  123. "ADMIN")) {
  124.         
  125.      case 'M':
  126.         master_admin();
  127.         break;
  128.  
  129.      case 'W':
  130.         kill_master_1(SIGALRM);
  131.         break;
  132.         
  133.      case 'E':
  134.         if (admin_confirm("Expire All Groups"))
  135.         send_master('X', -1L, 0L);
  136.         break;
  137.  
  138.      case 'I':
  139.         if (admin_confirm("INITIALIZATION, i.e. recollect all groups"))
  140.         send_master('R', -1L, 0L);
  141.         break;
  142.  
  143.      case 'G':
  144.         group_admin();
  145.         break;
  146.         
  147.      case 'L':
  148.         log_admin();
  149.         break;
  150.         
  151.      case 'S':
  152.         master_status();
  153.         break;
  154.  
  155.      case 'C':
  156.         show_config();
  157.         break;
  158.         
  159.      case 'U':
  160.         update_master();
  161.         break;
  162.  
  163.      case 'Z':
  164.         verbose = 0;
  165.         /* FALL THRU */
  166.      case 'V':
  167.         validate_groups();
  168.         verbose = 1;
  169.         break;
  170.  
  171.      case '=':
  172.         verbose = !verbose;
  173.         break;
  174.         
  175.      case 'Q':
  176.         if (was_raw) raw();
  177.         return;
  178.     }
  179.     }    
  180. }
  181.  
  182. static update_master()
  183. {
  184.     extern FILE *master_file;
  185.     register group_header *gh;
  186.     int ngp;
  187.     
  188.     ngp = master.number_of_groups;
  189.     
  190.     rewind(master_file);
  191.     db_read_master(master_file, &master);
  192.     
  193.     if (master.number_of_groups != ngp) {
  194.     printf("\nNumber of groups changed from %d to %d\n",
  195.            ngp, master.number_of_groups);
  196.     printf("You must restart nnadmin to access the new group(s)\n");
  197.     ngp = master.number_of_groups;
  198.     }
  199.     
  200.     ngp = 0;
  201.     
  202.     Loop_Groups_Header(gh) 
  203.     if (update_group(gh) < 0) ngp++;
  204.  
  205.     if (ngp) printf("There are %d blocked groups\n", ngp);
  206. }
  207.  
  208.     
  209. static master_admin()
  210. {
  211.     register char c;
  212.     int cur_group;
  213.     long value;
  214.     register group_header *gh;
  215.  
  216.     for (;;) {
  217.     switch (c = get_cmd(
  218. "\nA)ll E)mpty F)iles G)roup K)ill N)on-empty O)ptions S)tat T)race",
  219. "MASTER")) {
  220.  
  221.      case 'G':
  222.         cur_group = (int)get_entry("Group number",
  223.                   0L, (long)(master.number_of_groups - 1));
  224.         if (cur_group >= 0)
  225.         dump_m_entry(&active_groups[cur_group]);
  226.         break;
  227.  
  228.      case 'A':
  229.      case 'E':
  230.      case 'N':
  231.         cur_group = -1; 
  232.         s_keyboard = 0;
  233.         
  234.         while (++cur_group < master.number_of_groups) {
  235.         if (s_keyboard || s_hangup) break;
  236.         
  237.         gh = &active_groups[cur_group];
  238.         if (c == 'N' && gh->last_l_article == 0) continue;
  239.         if (c == 'E' && gh->last_l_article != 0) continue;
  240.         dump_m_entry(gh);
  241.         }
  242.         break;
  243.  
  244.      case 'F':
  245.         find_files(-1);
  246.         break;
  247.  
  248.      case 'O':
  249.         c = get_cmd("r)epeat_delay  e)xpire_level", "OPTION");
  250.         if (c != 'R' && c != 'E') break;
  251.         value = get_entry("Option value", 1L, 10000L);
  252.         if (value < 0) break;
  253.         send_master(tolower(c), value, 0L);
  254.         break;
  255.         
  256.      case 'S':
  257.         master_status();
  258.         break;
  259.  
  260.      case 'T':
  261.         send_master('T', 0L, 0L);
  262.         break;
  263.         
  264.      case 'K':
  265.         if (admin_confirm("Stop nn Master"))
  266.         kill_master_1(SIGHUP);
  267.         break;
  268.         
  269.      default:
  270.         return;
  271.     }
  272.     }
  273. }
  274.  
  275.  
  276. static log_admin()
  277. {
  278.     char command[FILENAME + 100], c;
  279.  
  280.     if (pre_input && *pre_input == NUL) {
  281.     c = SP;
  282.     goto log_tail;
  283.     }
  284.     
  285.  loop:
  286.  
  287.     c = get_cmd(
  288. "\n(1-9)tail A)dmin C)ollect E)rrors G)roup R)eports e(X)pire (*)all (@)clean",
  289. "LOG");
  290.     
  291.     if (c < SP || c == 'Q') return;
  292.  
  293.     if (c == '@') { 
  294.     if (admin_confirm("Truncation")) {
  295.         sprintf(command, 
  296. "cd %s && mv Log Log.old && ./log_entry A Truncated && chmod 666 Log",
  297.             lib_directory);
  298.         system(command);
  299.     }
  300.     return;
  301.     }
  302.     
  303.     if (c == 'G') {
  304.     char *groupname;
  305.     
  306.     groupname = get_groupname();
  307.     sprintf(command, "grep '%s' %s/Log | %s",
  308.         groupname, lib_directory, pager);
  309.     system(command);
  310.  
  311.     goto loop;
  312.     }
  313.  
  314.  log_tail:    
  315.     if (c == '$' || c == SP || isdigit(c)) {
  316.     int n;
  317.     
  318.     n = isdigit(c) ? 10 * (c - '0') : 10;
  319.     sprintf(command, "tail -%d %s/Log", n, lib_directory);
  320.     system(command);
  321.     goto loop;
  322.     }
  323.     
  324.     if (c == '*') {
  325.     c = '.';
  326.     }
  327.  
  328.     sprintf(command, "grep '^%c:' %s/Log | %s", c, lib_directory, pager);
  329.     system(command);
  330.     
  331.     goto loop;
  332. }
  333.  
  334.     
  335. static group_admin()
  336. {
  337.     char *groupname;
  338.     register group_header *gh;
  339.     
  340.  new_group:
  341.     groupname = get_groupname();
  342.     
  343.     gh = lookup(groupname);
  344.     if (gh == NULL) {
  345.     printf("No group named %s\n", groupname);
  346.     goto new_group;
  347.     }
  348.  
  349.     for (;;) {
  350.     switch (get_cmd(
  351. "\nC)lear_flag D)ata E)xpire F)iles G)roup H)eader S)et_flag R)ecollect",
  352. "GROUP")) {
  353.      case 'D':
  354.         dump_group(gh, 0);
  355.         break;
  356.         
  357.      case 'H':
  358.         dump_m_entry(gh);
  359.         break;
  360.  
  361.      case 'F':
  362.         find_files(gh->group_num);
  363.         break;
  364.         
  365.      case 'S':
  366.         flag_admin(gh, "Set", 1);
  367.         break;
  368.         
  369.      case 'C':
  370.         flag_admin(gh, "Clear", 0);
  371.         break;
  372.         
  373.      case 'R':
  374.         if (admin_confirm("Recolletion of Group"))
  375.         send_master('R', (long)gh->group_num, 0L);
  376.         break;
  377.  
  378.      case 'E':
  379.         if (admin_confirm("Expire Group"))
  380.         send_master('X', (long)gh->group_num, 0L);
  381.         break;
  382.         
  383.      case 'G':
  384.         goto new_group;
  385.         
  386.      default:
  387.         return;
  388.     }
  389.     }
  390. }
  391.  
  392.  
  393. static flag_admin(gh, mode_str, set_flag)
  394. group_header *gh;
  395. char *mode_str;
  396. int set_flag;
  397. {
  398.     char buffer[50];
  399.     int new_flag = 0;
  400.  
  401.     putchar(NL);
  402.     
  403.     dump_g_flag(gh);
  404.  
  405.     sprintf(buffer, "%s FLAG", mode_str);
  406.  
  407.     switch (get_cmd(
  408. "\nA)lways_digest N)ever_digest M)oderated C)ontrol no_(D)ir",
  409. buffer)) {
  410.  
  411.      default:
  412.     return;
  413.     
  414.      case 'M':
  415.     new_flag = G_MODERATED;
  416.     break;
  417.     
  418.      case 'C':
  419.     new_flag = G_CONTROL;
  420.     break;
  421.     
  422.      case 'D':
  423.     new_flag = G_NO_DIRECTORY;
  424.     break;
  425.     
  426.      case 'A':
  427.     new_flag = G_ALWAYS_DIGEST;
  428.     break;
  429.     
  430.      case 'N':
  431.     new_flag = G_NEVER_DIGEST;
  432.     break;
  433.     }
  434.     
  435.     if (new_flag & (G_CONTROL | G_NO_DIRECTORY))
  436.     if (!admin_confirm("Flag Change"))
  437.         new_flag = 0;
  438.     
  439.     if (new_flag) {
  440.     if (set_flag) {
  441.         if (gh->group_flag & new_flag)
  442.         new_flag = 0;
  443.         else {
  444.         send_master('S', (long)gh->group_num, (long)new_flag);
  445.         gh->group_flag |= new_flag;
  446.         }
  447.     } else {
  448.         if ((gh->group_flag & new_flag) == 0)
  449.         new_flag = 0;
  450.         else {
  451.         send_master('C', (long)gh->group_num, (long)new_flag);
  452.         gh->group_flag &= ~new_flag;
  453.         }
  454.     }        
  455.     }
  456.     
  457.     if (new_flag == 0)
  458.     printf("NO CHANGE\n");
  459.     else
  460.     dump_g_flag(gh);
  461. }
  462.  
  463.     
  464. static find_files(group)
  465. group_number group;
  466. {
  467.     char command[512];
  468.     
  469.     if (group < 0)
  470.     sprintf(command, "ls -l %s/DATA | %s", db_directory, pager);
  471.     else
  472.     sprintf(command, "ls -l %s/DATA/%d.*", db_directory, group);
  473.     system(command);
  474. }
  475.  
  476.  
  477. static master_status()
  478. {
  479.     int cur_group;
  480.     long articles, disk_use;
  481.     register group_header *gh;
  482.  
  483.     printf("\nMaster:\n");
  484.     printf("   last_scan:     %s\n", date_time(master.last_scan));
  485.     printf("   no of groups:  %d\n", master.number_of_groups);
  486.     printf("   next write:    %ld\n", master.next_group_write_offset);
  487.  
  488.     articles = disk_use = 0;
  489.     
  490.     for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
  491.     gh = &active_groups[cur_group];
  492.     
  493. #define DISK_BLOCKS(bytes) (((bytes) + 1023) / 1024)
  494.  
  495.     disk_use += DISK_BLOCKS(gh->index_write_offset);
  496.     disk_use += DISK_BLOCKS(gh->data_write_offset);
  497.  
  498.     articles += gh->last_l_article - gh->first_l_article + 1;
  499.     }
  500.     
  501.     printf("\n   Articles:   %ld\n", articles);
  502.     printf(  "   Disk usage: %ld k\n", disk_use);
  503. }
  504.  
  505. static show_config()
  506. {
  507.     extern char news_active[], news_directory[];
  508. #ifdef NNTP
  509.     extern char nntp_server[];
  510. #endif
  511.     
  512.     printf("\nConfiguration:\n\n");
  513.     printf("BIN:  %s\nLIB:  %s\nDB:   %s\nNEWS: %s\n",
  514.        BIN_DIRECTORY,
  515.        lib_directory, 
  516.        db_directory, 
  517.        news_directory);
  518.     
  519.     printf("ACTIVE: %s\n", news_active);
  520.     
  521. #ifdef NNTP
  522.     if (use_nntp)
  523.     printf("NNTP ACTIVE. server=%s\n", nntp_server);
  524.     else
  525.     printf("NNTP NOT ACTIVE\n");
  526. #endif
  527.  
  528. #ifdef NETWORK_DATABASE
  529.     printf("Database is machine independent (network format).\n");
  530. #ifdef NETWORK_BYTE_ORDER
  531.     printf("Local system assumes to use network byte order\n");
  532. #endif
  533. #else
  534.     printf("Database format is machine dependent (byte order and alignment)\n");
  535. #endif
  536.  
  537. #ifdef STATISTICS
  538.     printf("Recording usage statistics\n");
  539. #else
  540.     printf("No usage statistics are recorded\n");
  541. #endif
  542.  
  543.     printf("Default pager: %s\n", PAGER);
  544.     printf("Default printer: %s\n", PRINTER);
  545.     
  546.     printf("Mail delivery program: %s\n", REC_MAIL);
  547. #ifdef APPEND_SIGNATURE
  548.     printf("Query for appending .signature ENABLED\n");
  549. #else
  550.     printf("Query for appending .signature DISABLED\n");
  551. #endif    
  552.  
  553.     printf("Max pathname length is %d bytes\n", FILENAME-1);
  554. }
  555.        
  556. static dump_m_entry(gh)
  557. register group_header *gh;
  558. {
  559.     update_group(gh);
  560.     
  561.     printf("\n%s\t%d\n", gh->group_name, gh->group_num);
  562.     printf("first/last art: %06ld %06d\n",
  563.        gh->first_l_article, gh->last_l_article);
  564.     printf("   active info: %06ld %06d\n",
  565.        gh->first_article, gh->last_article);
  566.     printf("Offsets: index->%ld, data->%ld\n",
  567.        gh->index_write_offset,
  568.        gh->data_write_offset);
  569.     if (gh->group_flag)
  570.     dump_g_flag(gh);
  571. }
  572.  
  573. static dump_g_flag(gh)
  574. register group_header *gh;
  575. {
  576.     printf("Flags: ");
  577.     if (gh->group_flag & G_BLOCKED)     printf(" BLOCKED");
  578.     if (gh->group_flag & G_EXPIRE)     printf(" EXPIRE");
  579.     if (gh->group_flag & G_MODERATED)     printf(" MODERATED");
  580.     if (gh->group_flag & G_CONTROL)     printf(" CONTROL");
  581.     if (gh->group_flag & G_NO_DIRECTORY) printf(" NO_DIRECTORY");
  582.     if (gh->group_flag & G_ALWAYS_DIGEST) printf(" ALWAYS_DIGEST");
  583.     if (gh->group_flag & G_NEVER_DIGEST) printf(" NEVER_DIGEST");
  584.     printf("\n");
  585. }
  586.  
  587.  
  588. static dump_group(gh, validate)
  589. group_header *gh;
  590. int validate;
  591. {
  592.     FILE            *data, *ix;
  593.     data_header            hdr;
  594.     off_t              data_offset, real_offset, next_offset;
  595.     cross_post_number        cross_post;
  596.     article_number        first_article, next_article, this_art;
  597.     int                n, was_digest;
  598.     char            buffer[512];
  599.  
  600.     if (!validate) {
  601.     if (init_group(gh) <= 0)
  602.         printf("cannot access group %s\n", gh->group_name);
  603.  
  604.     update_group(gh);
  605.     }
  606.     
  607.     if (validate)
  608.     first_article = gh->first_l_article;
  609.     else
  610.     first_article = get_entry("First article",
  611.                   (long)gh->first_l_article,
  612.                   (long)gh->last_l_article);
  613.  
  614.     if (first_article < 0) first_article = gh->first_l_article;
  615.     if (first_article <= 0) first_article = 1;
  616.     
  617.     ix = open_data_file(gh, 'x', OPEN_READ);
  618.     if (ix == NULL) {
  619.     if (verbose) printf("NO INDEX FILE\n");
  620.     return 0;
  621.     }
  622.     next_offset = get_index_offset(gh, first_article);
  623.     fseek(ix, next_offset, 0);
  624.     
  625.     if (!db_read_offset(ix, &real_offset)) {
  626.     if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)first_article);
  627.     fclose(ix);
  628.     return 0;
  629.     }
  630.  
  631.     data_offset = real_offset;
  632.     fseek(ix, next_offset, 0);
  633.     
  634.     data = open_data_file(gh, 'd', OPEN_READ);
  635.     if (data == NULL) {
  636.     if (verbose) printf("NO DATA FILE\n");
  637.     fclose(ix);
  638.     return 0;
  639.     }
  640.  
  641.     
  642.     next_article = first_article;
  643.     s_keyboard = 0;
  644.     was_digest = 0;
  645.  
  646.     if (!validate) {
  647.     clrdisp();
  648.     pg_init(1, 1);
  649.     }
  650.     
  651.     while (next_article <= gh->last_l_article) {
  652.     if (s_hangup || s_keyboard) goto out;
  653.  
  654.     if (!db_read_offset(ix, &real_offset)) {
  655.         if (verbose)
  656.         printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article);
  657.         goto err;
  658.     }
  659.  
  660.     if (data_offset != real_offset)
  661.         goto ix_data_err;
  662.     
  663.     fseek(data, data_offset, 0);
  664.  
  665.      in_digest:
  666.     if (!validate && pg_scroll(6)) {
  667.         s_keyboard = 1;
  668.         goto out;
  669.     }
  670.     
  671.     next_offset = data_offset;
  672.     if (!db_read_art(data, &hdr, &data_offset)) {
  673.         if (real_offset == gh->data_write_offset || was_digest)
  674.         break;
  675.         
  676.         if (verbose) printf("No article header for article # %ld\n",
  677.                 (long)next_article);
  678.         goto err;
  679.     }
  680.     
  681.     if (hdr.dh_number) {
  682.         if (was_digest) {
  683.         next_article++;
  684.         if (!db_read_offset(ix, &real_offset)) 
  685.             goto ix_eof_err;
  686.         if (real_offset != next_offset)
  687.             goto ix_data_err;
  688.         }
  689.         
  690.         this_art = hdr.dh_number < 0 ? -hdr.dh_number : hdr.dh_number;
  691.     
  692.         if (this_art != next_article) {
  693.         if (this_art < next_article) {
  694.             if (verbose)
  695.             printf("Article # %ld out of sequence\n", 
  696.                    (long)this_art);
  697.             goto err;
  698.         }
  699. /*        
  700.         if (this_art == (next_article + 1)) 
  701.             printf("Article # %ld missing (OK)\n",
  702.                (long)next_article);
  703.         else
  704.             printf("Articles # %ld -> %ld missing (OK)\n",
  705.                (long)next_article, (long)this_art);
  706. */        
  707.         while (this_art > next_article) {
  708.             if (!db_read_offset(ix, &next_offset))
  709.             goto ix_eof_err;
  710.             if (next_offset != real_offset) 
  711.             goto ix_data_err;
  712.             next_article++;
  713.         }
  714.         }
  715.     }
  716.  
  717.     if (!validate)
  718.         printf("\noffset = %ld, article # = %ld\n",
  719.            (long)real_offset, (long)(hdr.dh_number));
  720.        
  721.     if (hdr.dh_lpos == (off_t)0) {    /* article not accessible */
  722.         if (verbose)
  723.         printf("# %ld: NO ARTICLE (ok)\n", (long)next_article);
  724.         continue;
  725.     }
  726.     
  727.     data_offset += hdr.dh_cross_postings * sizeof(cross_post_number)
  728.         + hdr.dh_sender_length 
  729.         + hdr.dh_subject_length;
  730.  
  731.     if (hdr.dh_cross_postings) {
  732.         if (!validate)
  733.         printf("xpost(%d):", hdr.dh_cross_postings);
  734.     
  735.         n = hdr.dh_cross_postings;
  736.         while (--n >= 0) {
  737.         if (fread((char *)&cross_post, sizeof(cross_post_number), 1, data)!= 1)
  738.             goto data_error;
  739. #ifdef NETWORK_DATABASE
  740. #ifndef NETWORK_BYTE_ORDER
  741.         cross_post = ntohl(cross_post);
  742. #endif
  743. #endif        
  744.         if (validate) {
  745.             if (cross_post >= master.number_of_groups) {
  746.             if (verbose)
  747.                 printf("xpost group out of range: %ld (article # %ld)\n",
  748.                    (long)cross_post, (long)next_article);
  749.             goto err;
  750.             }
  751.         } else
  752.             printf(" %d", cross_post);
  753.         } 
  754.         if (!validate) printf("\n");
  755.     }    
  756.  
  757.  
  758.     if (!validate) 
  759.         if (IS_DIGEST_HEADER(hdr))
  760.         printf("digest header ");
  761.         else
  762.         if (IS_SUB_DIGEST(hdr))
  763.         printf("digest article ");
  764.         else
  765.         printf("normal article ");
  766.     
  767.     if (!validate)
  768.         printf("ts=%lu hp=%ld, fp=+%d, lp=%ld, rep=%d, lines=%d\n",
  769.            (long unsigned)hdr.dh_date,
  770.            (long)hdr.dh_hpos, (int)hdr.dh_fpos, (long)hdr.dh_lpos,
  771.            hdr.dh_replies, hdr.dh_lines);
  772.     
  773.     if (hdr.dh_sender_length) {
  774.         if (fread(buffer, sizeof(char), (int)hdr.dh_sender_length, data)
  775.         != hdr.dh_sender_length) goto data_error;
  776.         buffer[hdr.dh_sender_length] = NUL;
  777.         if (!validate)
  778.         printf("Sender(%d): %s\n", hdr.dh_sender_length, buffer);
  779.     } else
  780.         if (!validate)
  781.         printf("No sender\n");
  782.     
  783.     if (hdr.dh_subject_length) {
  784.         if (fread(buffer, sizeof(char), (int)hdr.dh_subject_length, data)
  785.         !=  hdr.dh_subject_length) goto data_error;
  786.         buffer[hdr.dh_subject_length] = NUL;
  787.         if (!validate)
  788.         printf("Subj(%d): %s\n", hdr.dh_subject_length, buffer);
  789.     } else
  790.         if (!validate)
  791.         printf("No subject\n");
  792.  
  793.     if (IS_DIGEST_HEADER(hdr) || IS_SUB_DIGEST(hdr)) {
  794.         was_digest = 1;
  795.         goto in_digest;
  796.     }
  797.         
  798.     was_digest = 0;
  799.     next_article++;
  800.  
  801.     }
  802.  
  803. out:
  804.  
  805.     if (s_keyboard == 0 && data_offset != gh->data_write_offset) {
  806.     if (verbose)
  807.         printf("\n*** ERROR: Next data offset %ld wrong. real offset=%ld\n",
  808.            gh->data_write_offset, data_offset);
  809.     goto err;
  810.     }
  811.     
  812.     fclose(data);
  813.     fclose(ix);
  814.     return 1;
  815.  
  816.     
  817.     
  818.  data_error:
  819.     if (verbose) printf("\n*** END OF FILE on DATA FILE\n\n");
  820.     goto err;
  821.  
  822.  ix_eof_err:
  823.     if (verbose) printf("NO INDEX FOR ARTICLE %ld\n", (long)next_article);
  824.     goto err;
  825.     
  826.  ix_data_err:
  827.     if (verbose) {
  828.     printf("\n*** OFFSET ERROR in article # %ld offsets:\n",
  829.            (long)next_article);
  830.     printf("    Calcuated offset %ld differs from index %ld\n",
  831.            (long)data_offset, (long)real_offset);
  832.     }
  833.     goto err;
  834.     
  835.  err:
  836.             
  837.     fclose(data);
  838.     fclose(ix);
  839.     return 0;
  840. }
  841.  
  842.  
  843. send_master(command, arg1, arg2)
  844. char command;
  845. long arg1, arg2;
  846. {
  847.     FILE *gate;
  848.  
  849.     gate = open_file(relative(lib_directory, "GATE"), OPEN_APPEND);
  850.     
  851.     if (gate == NULL) {
  852.     printf("Cannot send to master\n");
  853.     return;
  854.     }
  855.     
  856.     fprintf(gate, "%c;%ld;%ld;%s %s;\n",
  857.         command, arg1, arg2, user_name(), date_time((time_t)0));
  858.     
  859.     fclose(gate);
  860.  
  861.     log_entry('A', "SEND %c %ld %ld", command, arg1, arg2);
  862. }
  863.  
  864.  
  865.     
  866.     
  867. /* fake this for visit_active_file() */
  868.  
  869. /*ARGSUSED*/
  870. group_header *add_new_group(name)
  871. char *name;
  872. {
  873.     return NULL;
  874. }
  875.  
  876.  
  877. /*
  878.  *    make consistency check on all groups
  879.  */
  880.  
  881.  
  882. static file_error(gh, d_or_x, write_offset)
  883. group_header *gh;
  884. char d_or_x;
  885. off_t write_offset;
  886. {
  887.     FILE *f;
  888.     
  889.     f = open_data_file(gh, d_or_x, OPEN_READ);
  890.  
  891.     if (f == NULL) {
  892.     if (verbose)
  893.         printf("FILE '%c' NOT FOUND\n", d_or_x);
  894.     } else {
  895.     fseek(f, (off_t)0, 2);
  896.     if (ftell(f) >= write_offset) {
  897.         fclose(f);
  898.         return 0;
  899.     }
  900.  
  901.     if (verbose)
  902.         printf("FILE '%c' IS SHORTER THAN NEXT WRITE OFFSET\n", d_or_x);
  903.     fclose(f);
  904.     }
  905.     
  906.     return 1;
  907. }
  908.     
  909.  
  910. validate_groups()
  911. {
  912.     register group_header     *gh;
  913.     group_number         num;
  914.     import  char *pname;
  915.     
  916.     if (strcmp(pname, "nnadmin")) {
  917.     printf("You can only run VALIDATION from nnadmin\n");
  918.     return;
  919.     }
  920.     
  921.     s_keyboard = 0;
  922.  
  923.     Loop_Groups_Number(num) {
  924.  
  925.     if (s_hangup || s_keyboard) break;
  926.     
  927.     gh = &active_groups[num];
  928.     if (init_group(gh) <= 0) continue; /* no directory */
  929.  
  930.     if (verbose) { printf("\r%s: ", gh->group_name); clrline(); }
  931.     update_group(gh);
  932.     
  933.     if (gh->group_flag & G_BLOCKED) {
  934.         if (verbose) printf("BLOCKED\n");
  935.         continue;
  936.     }
  937.     
  938.     /*
  939.      *    Check for major inconcistencies.
  940.      *    Sometimes, news expire will reset article numbers
  941.      *    to start from 0 again
  942.      */
  943.  
  944.     if (gh->last_l_article == 0) {
  945.         continue;
  946.     }
  947.     
  948.     if (gh->first_article > (gh->last_l_article + 1) ||
  949.         gh->last_l_article  > gh->last_article || 
  950.         gh->first_l_article > gh->first_article) {
  951.  
  952.       if (verbose)
  953.         printf("RENUMBERING OF ARTICLES (active=%ld..%ld master=%ld..%ld)",
  954.            gh->first_article, gh->last_article,
  955.            gh->first_l_article, gh->last_l_article);
  956.         goto ask_collect;
  957.     }
  958.  
  959.     /*
  960.      *    Check existence and sizes of data files
  961.      */
  962.  
  963.     if (file_error(gh, 'x', gh->index_write_offset))
  964.         goto ask_collect;
  965.     
  966.     if (file_error(gh, 'd', gh->data_write_offset))
  967.         goto ask_collect;
  968.  
  969.     if (!dump_group(gh, 1))
  970.         goto ask_collect;
  971.  
  972.     if (!verbose) continue;
  973.     
  974.     if (gh->first_article > gh->first_l_article)
  975.         printf("unexpired articles: %ld\n",
  976.            (long)(gh->first_article - gh->first_l_article));
  977.     else
  978.         printf("OK\r");
  979.     continue;
  980.     
  981.      ask_collect:
  982.     log_entry('V', "Database inconsistency in group %s", gh->group_name);
  983.     
  984.     if (admin_confirm("\nRepair group"))
  985.         send_master('R', (long)gh->group_num, 0L);
  986.     }
  987. }
  988.  
  989.  
  990.  
  991. kill_master_1(sig)
  992. int sig;
  993. {
  994.     if (kill_master(sig)) {
  995.     if (verbose) printf("sent signal %d to master\n", sig);
  996.     } else
  997.     if (errno == ESRCH) {
  998.     if (verbose) printf("master is not running\n");
  999.     } else
  1000.     printf("cannot signal master (errno=%d)\n", errno);
  1001. }
  1002.